package com.new4net.exe_stream.meta.impl;

import com.new4net.common.Tuple;
import com.new4net.exe_stream.meta.ExecutionContext;
import com.new4net.exe_stream.service.DomainModelDiagram;
import com.new4net.exe_stream.service.ExecutionInstructionService;
import com.new4net.exe_stream.service.ExecutionLogService;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;

public class SimpleExecutionContext implements ExecutionContext {
    private ExecutorService executorService;
    private ExecutionLogService executionLogService;
    private DomainModelDiagram domainModelDiagram;

    private ExecutionInstructionService executionInstructionService;

    private List<Tuple<InstructionContext, Integer>> instructionContexts = new ArrayList<>();

    private Map<String, Object> globalParams = new HashMap<>();


    public SimpleExecutionContext(
            ExecutorService executorService
            , ExecutionLogService executionLogService
            , DomainModelDiagram domainModelDiagram
            , ExecutionInstructionService executionInstructionService
    ) {
        this.executorService = executorService;
        this.executionLogService = executionLogService;
        this.domainModelDiagram = domainModelDiagram;
        this.executionInstructionService = executionInstructionService;
    }


    @Override
    public ExecutorService getExecutor() {
        return executorService;
    }

    @Override
    public ExecutionLogService getExecutionLogService() {
        return executionLogService;
    }

    @Override
    public DomainModelDiagram getDomainModelDiagram() {
        return domainModelDiagram;
    }

    @Override
    public ExecutionInstructionService getExecutionInstructionService() {
        return executionInstructionService;
    }


    @Override
    public <R> R getReturnVal() {
        InstructionContext lastInstructionContext = getLastInstructionContext();
        if (lastInstructionContext == null) {
            return null;
        }

        return lastInstructionContext.getReturnVal();
    }

    @Override
    public InstructionContext buildInstructionContext() {
        SimpleInstructionContext simpleInstructionContext = new SimpleInstructionContext();
        simpleInstructionContext.executionContext=this;
        return simpleInstructionContext;
    }

    private InstructionContext getLastInstructionContext() {
        int size = instructionContexts.size();
        if (size > 0) {
            return instructionContexts.get(size - 1).getT1();
        }
        return null;
    }


    @Override
    public <R> R getParam(String name) {

        return getParam(name, null);
    }


    @Override
    public ExecutionContext clone() {
        SimpleExecutionContext clone = new SimpleExecutionContext(executorService
                , executionLogService
                , domainModelDiagram
                , executionInstructionService);

        for (Tuple<InstructionContext, Integer> context : instructionContexts) {
            clone.instructionContexts.add(context);
        }
        return clone;
    }


    @Override
    public void addInstructionContext(InstructionContext instructionContext) {
        if(instructionContext instanceof SimpleInstructionContext){
            ((SimpleInstructionContext) instructionContext).executionContext=null;
        }
        instructionContexts.add(new Tuple<>(instructionContext, instructionContexts.size()));
    }

    @Override
    public void addParam(String name, String value) {
        globalParams.put(name, value);
    }

    @Override
    public <R> R getParam(String name, InstructionContext in) {
        int position = 0;
        if (in != null) {
            position = getPosition(in);
        }

        if (position > 0) {
            int size = instructionContexts.size();
            if (size > 0) {
                for (int i = size - 1; i >= 0; i--) {
                    if (i <= position - 1) {
                        InstructionContext instructionContext = instructionContexts.get(i).getT1();
                        Object param = instructionContext.getParam(name);
                        if (param != null) {
                            return (R) param;
                        }
                    }

                }

            }
        }
        return (R) globalParams.get(name);

    }

    private int getPosition(InstructionContext in) {
        for (Tuple<InstructionContext, Integer> tuple : instructionContexts) {
            if (tuple.getT1() == in) {
                return tuple.getT2();
            }
        }
        return instructionContexts.size();
    }


    public static class SimpleInstructionContext implements InstructionContext {
        private ExecutionContext executionContext;
        private Map<String,Object> params = new HashMap<>();

        private SimpleInstructionContext(){

        }

        @Override
        public <R> R getReturnVal() {
            return getParam("return");
        }

        @Override
        public <R> void addParam(String name, R value) {
            params.put(name,value);
        }

        @Override
        public <R> R getParam(String name) {
            Object o = params.get(name);
            if(o==null &&executionContext!=null){
               o= executionContext.getParam(name,this);
            }
            return (R) o;
        }

        @Override
        public void removeParam(String name) {
            params.remove(name);
        }

        @Override
        public void clear() {
            params.clear();
        }




    }
}
